<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
      div {
                  height: 100vh;
                  background: rgba(0, 0, 0, .7);
                  color: #fff;
                  line-height: 100vh;
                  text-align: center;
                  font-size: 20vh;
              }
      
              .a-img1 {
                  background-image: url(https://images.pexels.com/photos/1097491/pexels-photo-1097491.jpeg);
                  background-attachment: fixed;
                  background-size: cover;
                  background-position: center center;
              }
      
              .a-img2 {
                  background-image: url(https://images.pexels.com/photos/2437299/pexels-photo-2437299.jpeg);
                  background-attachment: fixed;
                  background-size: cover;
                  background-position: center center;
              }
      
              .a-img3 {
                  background-image: url(https://images.pexels.com/photos/1005417/pexels-photo-1005417.jpeg);
                  background-attachment: fixed;
                  background-size: cover;
                  background-position: center center;
              }
      </style>
</head>
<body>
    <div class="a-text">1</div>
    <div class="a-img1">2</div>
    <div class="a-text">3</div>
    <div class="a-img2">4</div>
    <div class="a-text">5</div>
    <div class="a-img3">6</div>
    <div class="a-text">7</div>
    <script>
      Promise.resolve().then(() => {
          console.log(0);
          return Promise.resolve(4);
      }).then((res) => {
          console.log(res)
      })

      Promise.resolve().then(() => {
          console.log(1);
      }).then(() => {
          console.log(2);
      }).then(() => {
          console.log(3);
      }).then(() => {
          console.log(5);
      }).then(() =>{
          console.log(6);
      })

      //微 0 1 4

        /**
         * 题1
        */
        function Foo() {
          getName = function () { console.log (1); }; 
          return this;
        }
        Foo.getName = function () { console.log (2);};
        Foo.prototype.getName = function () { console.log (3);};
        var getName = function () { console.log (4);};
        function getName() { console.log (5);}

        //编译 函数和表达式不同，函数直接提升到顶部，函数表达式声明和赋值分开 
        //function Foo
        //function getName
        //getName 变量  变量声明不能覆盖函数声明，但可以赋值时修改，已经声明过为函数，不再重复声明

        //赋值  getName 被重新赋值给 function () { console.log (4);}

        Foo.getName();   //2  Foo为函数，但可以新增内部属性getName，所以为2
        getName();       //4  赋值时  getName 变成了 function () { console.log (4);}
        Foo().getName(); //1  Foo()执行完后，内部 getName没有var声明，则变为全局属性，this指向window,相当于window.getName,此时将上一步全局中的getName重新赋值 getName = function () { console.log (1); }; 
        getName();   //1  执行全局window.getName,上一步已经为1，再次执行还为1     
        new Foo.getName(); //2  考察运算符优先级（成员访问，执行函数>new无参），()优先级大于new操作符，先执行的还是Foo.getName()第一步为2，此时也为2 ，执行new时将Foo.getName()构造函数来执行  
        new Foo().getName(); //3  考察运算符优先级，（new() 有参，成员访问，执行函数优先级相同）执行顺序从左向右，new Foo()创建实例，返回对象实例，this实例上没有 getName，所以从原型上找，所以为3     
        new new Foo().getName(); //3 考察的是运算符优先级

        //构造函数的返回值
        //1.没有返回值，默认返回实例化对象
        //2.有返回值，但返回值为基本类型，则与没有返回值相同，返回默认的实例化对象
        //3.有返回值，且返回的是引用类型值，则返回此引用类型 

        //new的有参和无参形式
        //有参 new A()
        //无参 new A


        /**
        * 题2  考察赋值运算的右结合性
        */
        var o1={x:1}; 
        var o2=o1;       // o1 = o2 = {x:1}
        o1.a=o1={x:2};   //o1.a=(o1={x:2})
        //第一步： 先走o1.a   o1新增一个属性a(undefined),指向一个内存地址,假设为A
        //第二步： o1 = {x:2}, o1等于一个新对象，赋值操作返回一个指向{x:2}的引用, A={x:2}
        console.log(o1.a)  //undefined
        console.log(o2.a)  //{x:2}

        /**
         * 题3  
         */
        var b=10;
        (function b(){
          b=20          //先声明一个外部全局变量是b，赋值为10,进入立即执行函数，创建一个新的函数作用域，现在当前作用域找，b为函数m按照就近原则查找，所以此时b为函数
          console.log(b)  //如果内部b为var声明，则输出20
        })()
        console.log(b)  // 10
        
        /**
         * 使用Symbol私有化
         */
        let x = Symbol('foo');
        class A{
          constructor(){
            console.log('初始化')  
            this[x]();
          }
          [x](){   //Symbol的增强写法
              console.log("123")
          }
        }
        let a = new A();
        console.log(a[x])
        //延时函数1
        function sleep(time){
          return new Promise(resolve=>{
            setTimeout(resolve,time)
          })  
        }
        //延时函数2 利用date
        function sleep1(time){
          let endTime = new Date().getTime() + parseInt(time,10);
          while(new Date().getTime() < endTime){}
        }
        console.log(new Date().getTime(),'开始延时')
        sleep1(1000)
        console.log(new Date().getTime(),'结束延时')
        /**
         * 手写promise,promise.all,promise.race,promise.finally，控制并发数量
         */        
        class Promise1{
          constructor(executor){
            this.status = 'pendding';
            this.value = undefined;
            this.reason = undefined
            this.onResolved = [];
            this.onRejected = [];
            function resolve(value){
              if(this.status == 'pendding'){
                this.status == 'fulfilled';
                this.value = value;
                this.onResolved.forEach((cb)=>cb(value))
              }
            }
            function reject(reason){
              if(this.status == 'pendding'){
                this.status == 'rejected';
                this.reason = reason;
                this.onRejected.forEach((cb)=>cb(reason))
              }
            }
            try {
              typeof executor == 'function' &&  executor(resolve,reject);
            } catch (error) {
              reject(error)
            }
          }
          then(onFulfilled, onRejected){
            typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
            typeof onRejected !== 'function' ? onRejected = error => error : null
            return new Promise((resolve,reject)=>{
              const resolveFn = (value)=>{
                const x = onFulfilled(value);
                try {
                  x instanceof Promise ? x.then(resolve,reject):resolve(value)
                } catch (error) {
                  reject(error) 
                }
              }
              const rejectFn=(value)=>{
                const x = onRejected(value);
                try {
                  x instanceof Promise ? x.then(resolve,reject):resolve(value)
                } catch (error) {
                  reject(error) 
                }
              }
            })
          }
          catch(){

          }
          finally(){

          }
          static resolve(value){
            
          }
          static reject(error){

          }
          static all(promiseArr){
            let count = 0;
            let result = [];
            return new Promise((resolve,reject)=>{
              if (!promiseArr.length) {  //不是数组
                return resolve(result)
              }               
              promiseArr.forEach((p,i)=>{
                 Promise.resolve(p).then(value=>{
                    count++;
                    result[i] = value;
                    if(promiseArr.length == count){
                      resolve(result);
                    }
                 },error=>{
                   reject(error)
                 }) 
              })
            })  
          }
          static race(promiseArr){
            return new Promise((resolve,reject)=>{
              promiseArr.forEach(p=>{
                Promise.resolve(p).then(value=>{
                  resolve(value) 
                })
              })
            }) 
          }
        }  
        /**
         * 排序算法
         */
         const arr = [98, 42, 25, 54, 15, 3, 25, 72, 41, 10, 121];
        //1、排序算法之冒泡排序
        function bubbleSort(array){
          for(let i=0;i<array.length;i++){
            for(let j =0;j<array.length - 1;j++){
              if(array[j]>array[j+1]){
                [array[j],array[j+1]] = [array[j+1],array[j]]  //交换
              }
            }  
          }
          return array
        }
        console.log('冒泡排序结果',bubbleSort(arr))
        //2、排序算法之选择排序  单向选择 双向选择
        //假设数组第一项为最小，与数组其余n-1个元素比较，如果后面有比此项小，则交换位置;后续从下一个元素重复在剩余的n-2 n-3个元素中找最小
        function selectSort(array){
          let minIndex;
          for(let i=0;i<array.length;i++){
             minIndex = i;
             for(let j=i+1;j<array.length;j++){
               if(array[minIndex]>array[j]){
                  minIndex =j; 
               }
             }
            [array[minIndex],array[i]] = [array[i],array[minIndex]];  
          }
          return array
        }
        //选择排序 双向选择
        function selectSort1(array){
          return array
        }
        console.log('选择排序结果',selectSort(arr))
        //3、排序算法之插入排序
        //假设第0个元素为已排序元素，第一个元素与之比较，比之小则将此元素与之交换位置，当前元素每次与左侧已排序部分比较，在合适的位置插入，其余元素一次往后挪动位置
        function insertSort(array){
          for(let i=0;i<array.length;i++){
            for(let j=i;j>0;j--){
              if(array[j]<array[j-1]){ 
                 [array[j],array[j-1]] = [array[j-1],array[i]]
              }else{
                break;
              }
            }
          } 
          return array;
        }
        console.log('插入排序结果',insertSort(arr))
        //4、排序算法之快速排序
        function quickSort(array){
          if(array.length<=1){
            return array;
          }
          let left=[],right=[],pivot=array[0];  //基准值pivot为数组第一项，比基准值小的放在left,大的放在right
          //另外一种
          // const pivotIndex = Math.floor(array.length / 2);
          // const pivot = array.splice(pivotIndex, 1)[0];  for循环从0开始
          for(let i=1;i<array.length;i++){
            if(array[i]<pivot){
              left.push(array[i])
            }else{
              right.push(array[i])
            }
          }
          return [...quickSort(left),pivot,...quickSort(right)]  
         //return quickSort(left).concat(pivot,quickSort(right))
        } 
        console.log('快速排序结果',quickSort(arr));
        console.log('js sort方法排序结果',arr.sort((a,b)=>a-b));
        //5、排序算法之归并排序

        /**
         * 动态规划 斐波那契数列 最长回文子串 最长路径 最小路径和 最长公共子序列  01背包问题  接雨水
         */
         //斐波那契数列  递归法
         function fblq(n){
           if(n<2){
             return n
           }else{
             return fblq(n-1)+fblq(n-2)
           }
         }
         console.log('递归：斐波那契数列',fblq(10))
         //斐波那契数列 动态规划
         function dyFib(n){
            let arr = new Array(n).fill(0);
            if(n<2){
              return n;
            }else{
              arr[1] = 1;
              arr[2] = 2;
              for(var i=3;i<n;i++){
                arr[i] = arr[i-1] + arr[i-2]
              }
              return arr[i-1]
            }
         }
         console.log('动态规划：斐波那契数列',dyFib(10))

         //最长公共子串
         function lcs(str1,str2){
           var max =0;
           var index =0;
           var lcsArr = new Array(str1.length+1);
           for(let i=0;i<=str1.length;i++){
             lcsArr[i]=new Array(str2.length).fill(0);
           }
           for(let i=0;i<=str1.length;i++){
             for(let j=0;j<=str2.length;j++){
                if(i==0 || j==0){
                  lcsArr[i][j] = 0;
                } else{
                  if(str1[i-1] == str2[j-1]){
                    lcsArr[i][j] = lcsArr[i-1][j-1] + 1;
                  }else{
                    lcsArr[i][j] = 0;
                  }
                }
                if(max < lcsArr[i][j]){
                  max = lcsArr[i][j];
                  index = i;
                }
             }
           }
           console.log(lcsArr)
           var str = "";
           if(max == 0){
             str = "" 
           }else{
             for(let i = index - max;i<=max;i++){
               str+= str2[i]
             }
           }
           return str;
         }   

         console.log('最长公共子串',lcs('abbcc','dbbcc'))
         console.log('最长公共子串',lcs('raven','havoc'))
        /**
         * 贪心算法  01背包
         */ 
        //背包问题递归解法
        var size = [3,4,7,8,9];  //物品的大小
        var values = [4,5,10,11,13];  //物品的价值
        var capacity = 16;   //背包的容量
        function knapsack(capacity,size,values,n){
          if(capacity == 0 || n == 0){
             return 0;    
          }else{
            if(size[n-1] > capacity){
              return knapsack(capacity,size,values,n-1)
            }else{
              return Math.max(values[n-1] + knapsack(capacity - size[n-1],size,values,n-1),knapsack(capacity,size,values,n-1))
            }
          }
        }
        console.log(knapsack(capacity,size,values,5))
        //背包问题 贪心算法
        function knapsack1(capacity,size,values,n){
          var load =0;
          var value =0;
          var i=0;
          while(load < capacity && i<n){
            if(size[i] <= capacity -load){
              load += size[i]
              value+= values[i]
            }else{
              var r = (capacity - load) / size[i] 
              value+= r * values[i]
              load+=size[i]
            }   
            ++i;
          }
          return value
        }
        console.log('贪心算法',knapsack1(capacity,size,values,5))
        /**
         * 数组去重
         */ 
        
        /**
         * 惰性函数
         */

        /**
         * 函数柯里化和反柯里化 
         * 柯里化：把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，并且返回接受余下的参数而且返回结果的新函数的技术。
         * 优点：参数复用，延迟执行
         * 函数本身的length属性代表的是函数定义时给出的参数个数
         */

        function currying(fn,...args1){  //args1表示第一次传入的参数
            return (...args2)=>{
              if(fn.length > args2.length){
                return currying.call(this,fn,...args2)  
              } 
              return fn.apply(this,args1)
            }
        }
        const sum = (a, b, c) => a + b + c;
        // const currySum = currying(sum);
        // console.log(currySum(1)(2))
        // console.log(currySum(1,2,3)(4))
        // console.log(currySum(1,2,3,4,5))
        // var curry = (fn, arr = []) => (...args) => (
        //   arg => arg.length === fn.length
        //     ? fn(...arg)
        //     : curry(fn, arg)
        // )([...arr, ...args])

        var uncurrying= function (fn) {
          return function () {        
              return Function.prototype.call.apply(fn,arguments);
          }
        };
        /**
         *  深度优先和广度优先
         */ 
        //深度优先算法

        function DFS(){
           
        }
        //广度优先算法
        function BFS(){
          
        }  
        /**
         * 手写promise,promise.all,promise.race,promise.finally
         */
        
        /**
         * EventEmitter
         */
        class EventEmitter{
          constructor(maxListenser){
            this.events = {},
            this.maxListenser = maxListenser || 10
          }
          emit(event,...args){
            const cbs = this.events[event]
             if(cbs){
                cbs.forEach(cb => {
                  cb.apply(this,args)
                });  
             }else{
               console.error('没有对应的监听事件')
             } 
          }
          on(event,cb){
            if(!this.events[event]){
              this.events[event] = []
            }
            if(this.events[event].length > this.maxListenser){
              console.error(`当前事件${event}超出最大监听数量`)
              return this;
            }
            this.events[event].push(cb);
            return this;
          }
          off(event,cb){
            if(!this.events[event]){
              return this; 
            }
            if(!cb){
              this.events[event] = null 
            }else{
              this.events[event] = this.events[event].filter((item)=>item!=cb)
            }
            return this;
          }
          once(event,cb){
            const fn = (...args)=>{
              this.off(event,fn)
              cb.apply(this,args);
            }
            this.on(event,fn)
            return this;
          }
        }
        const eventHub = new EventEmitter();
        eventHub.on('show',()=>{console.log('on show1 event exec')})
        eventHub.on('show',()=>{console.log('on show2 event exec')})
        eventHub.emit('show')
        eventHub.emit('show')   //on绑定的事件可以重复触发多次
        eventHub.off('show')    //off解绑事件
        eventHub.emit('show')   //再次触发就找不到事件绑定
        eventHub.once('hide',()=>{console.log('once hide1 event exec')})  //绑定事件1
        eventHub.once('hide',()=>{console.log('once hide2 event exec')})  //绑定事件2
        eventHub.emit('hide')   //只执行一次
        eventHub.emit('hide')   //再次执行就找不到绑定事件  
        console.log(eventHub)
        
        /**
         * 节流防抖
         */

        /**
         * 设计模式：发布订阅模式、观察者模式
         */
        //发布订阅模式
        //观察者模式 

        /**
         * ES5/ES6实现继承
         */
        

        /**
         * http请求封装
         */
        function ajax(url,options){
            const xhr = new XMLHttpRequest();
            options = options || {};
            xhr.open('GET',url);
            xhr.send();
            xhr.onreadystatechange = function(res){
               if(xhr.readyState == 4){
                  if(xhr.status == 200){
                    //  resolve(res)
                  } 
               }  
            }

        }
        /**
         * 实现并集，交集，差集
         */
        let aa = new Set([1,2,3]);
        let bb = new Set([2,3,4])  
        //并集
        let cc = new Set([...aa,...bb]);
        console.log('并集',cc)
        //交集
        let dd = new Set([...aa].filter(item=>bb.has(item)));
        console.log('交集',dd) 
        //差集
        let ee = new Set([...aa].filter(item=>!bb.has(item)));
        console.log('差集',ee) 
        
        /**
         *  promise处理异常
         * 1 try catch,catch只能接收到throw new Error()的错误
         * 2 promise内部的错误通过reject方法，在then方法的第二个回调接收处理错误或被catch方法接收
         * 3 promise all 处理错误，要看各promise是否自带catch，有则自行处理；否则走promise.all 的错误处理
         */
        try {
          new Promise((resolve,reject)=>{
            throw new Error('DD')
            reject('ddd')
          }).then((res)=>{
            console.log(res)
          }).catch((err)=>{
            console.log(err)
          })
        } catch (error) {
          console.log(error)
        }
        //Promise.all，p1/p2有一个报错，如果各自都有catch,则不会调用Promise.all的catch
        //promises如果包含不是promise,或thenable的对象，比如基本类型数据时，会转为resolved 的promise
        const p1 = new Promise((resolve,reject)=>{
          resolve(1)
        }).catch((err)=>{
          console.error('p1 catch',err)
        })
        // const p2 = new Promise((resolve,reject)=>{
        //    throw new Error('出错了')
        // })
        // .catch((err)=>{
        //   console.error('p2 catch',err)
        // })
        Promise.all([p1,111]).then((res)=>{
          console.log('promise all 结果',res)
        }).catch((err)=>{
          console.log('promise all catch',err)
        })
    </script>
</body>
</html>